package io.gitee.mingbaobaba.security.core.repository;

import io.gitee.mingbaobaba.security.core.constants.ErrorCodeConstant;
import io.gitee.mingbaobaba.security.core.constants.SecurityConstant;
import io.gitee.mingbaobaba.security.core.domain.SecuritySession;
import io.gitee.mingbaobaba.security.core.domain.SecurityToken;
import io.gitee.mingbaobaba.security.core.utils.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;

/**
 * <p>默认基于内存存储实现</p>
 *
 * @author yingsheng.ye
 * @version 1.0.0
 * @since 2023/8/28 15:56
 */
@Slf4j
public class SecurityRepositoryDefaultImpl implements SecurityRepository {

    /**
     * SecuritySession存储
     */
    private static final AtomicReference<Map<String, SecuritySession>> sessionMap = new AtomicReference<>(new ConcurrentHashMap<>());

    /**
     * SecurityToken存储
     */
    private static final AtomicReference<Map<String, SecurityToken>> tokenMap = new AtomicReference<>(new ConcurrentHashMap<>());

    /**
     * 记录数据版本信息
     */
    private static final AtomicReference<Map<String, Long>> versionMap = new AtomicReference<>(new ConcurrentHashMap<>());


    @Override
    public SecuritySession getSecuritySessionByLoginId(String loginId) {
        SecuritySession securitySession = sessionMap.get().get(loginId);
        if (null == securitySession) {
            return null;
        }
        Long timeout = securitySession.getTimeout();
        if (!SecurityConstant.NON_EXPIRING.equals(timeout)) {
            //判断是否超时
            String createTime = securitySession.getCreateTime();
            if (DateUtil.strToLocalDateTime.apply(createTime).plusSeconds(timeout).isBefore(LocalDateTime.now())) {
                //已超时，销毁security session
                securitySession.destroySecuritySession();
                return null;
            }
        }
        Long version = versionMap.get().get(loginId);
        securitySession.setVersion(null == version ? 0L : version);
        return securitySession;
    }

    @Override
    public Long getSessionTimeoutByLoginId(String loginId) {
        SecuritySession securitySession = getSecuritySessionByLoginId(loginId);
        if (null == securitySession) {
            return null;
        }
        Long timeout = securitySession.getTimeout();
        if (!SecurityConstant.NON_EXPIRING.equals(timeout)) {
            //计算剩余时间
            String createTime = securitySession.getCreateTime();
            long second = DateUtil.strToLocalDateTime.apply(createTime).plusSeconds(timeout)
                    .toEpochSecond(ZoneOffset.ofHours(8))
                    - LocalDateTime.now().toEpochSecond(ZoneOffset.ofHours(8));
            return second > 0 ? second : 0L;
        }
        return timeout;
    }

    @Override
    public boolean saveSecuritySession(SecuritySession session) {
        Long version = versionMap.get().get(session.getLoginId());
        version = null == version ? 0L : version;
        if (null != session.getVersion() &&
                !session.getVersion().equals(version)) {
            log.warn("版本验证异常:{}", ErrorCodeConstant.CODE_VERSION_ERR);
        }
        session.setVersion(++version);
        versionMap.get().put(session.getLoginId(), session.getVersion());
        sessionMap.get().put(session.getLoginId(), session);
        return true;
    }

    @Override
    public boolean removeSecuritySessionByLoginId(String loginId) {
        SecuritySession securitySession = sessionMap.get().get(loginId);
        if (null == securitySession) {
            return false;
        }
        securitySession.getTokenInfoList().forEach(item -> removeTokenByTokenValue(item.getToken()));
        sessionMap.get().remove(loginId);
        return true;
    }

    @Override
    public SecurityToken getSecurityTokenByTokenValue(String tokenValue) {
        return tokenMap.get().get(tokenValue);
    }

    @Override
    public String getActivityTimeByTokenValue(String tokenValue) {
        SecurityToken securityToken = getSecurityTokenByTokenValue(tokenValue);
        return Objects.isNull(securityToken) ? null : securityToken.getActivityTime();
    }

    @Override
    public Long getTokenTimeOutByTokenValue(String tokenValue) {
        SecurityToken securityToken = getSecurityTokenByTokenValue(tokenValue);
        if (Objects.isNull(securityToken)) {
            return null;
        }
        String createTime = securityToken.getCreateTime();
        //计算超时时间
        Long timeout = securityToken.getTimeout();
        if (SecurityConstant.NON_EXPIRING.equals(timeout)) {
            return SecurityConstant.NON_EXPIRING;
        }
        //计算剩余时间
        long second = DateUtil.strToLocalDateTime.apply(createTime).plusSeconds(timeout)
                .toEpochSecond(ZoneOffset.ofHours(8))
                - LocalDateTime.now().toEpochSecond(ZoneOffset.ofHours(8));
        return second > 0 ? second : 0L;
    }

    @Override
    public Long getTokenActivityTimeOutByTokenValue(String tokenValue) {
        SecurityToken securityToken = getSecurityTokenByTokenValue(tokenValue);
        if (Objects.isNull(securityToken)) {
            return null;
        }
        String activityTime = securityToken.getActivityTime();
        //计算超时时间
        Long timeout = securityToken.getActivityTimeout();
        if (SecurityConstant.NON_EXPIRING.equals(timeout)) {
            return SecurityConstant.NON_EXPIRING;
        }
        //计算剩余时间
        long second = DateUtil.strToLocalDateTime.apply(activityTime).plusSeconds(timeout)
                .toEpochSecond(ZoneOffset.ofHours(8))
                - LocalDateTime.now().toEpochSecond(ZoneOffset.ofHours(8));
        return second > 0 ? second : 0L;
    }

    @Override
    public boolean saveToken(SecurityToken token) {
        tokenMap.get().put(token.getToken(), token);
        return true;
    }

    @Override
    public boolean removeTokenByTokenValue(String tokenValue) {
        SecurityToken securityToken = tokenMap.get().remove(tokenValue);
        return Objects.nonNull(securityToken);
    }

    @Override
    public boolean renewalTokenByTokenValue(String tokenValue) {
        SecurityToken securityToken = tokenMap.get().remove(tokenValue);
        securityToken.setActivityTime(DateUtil.formatDateTime.apply(new Date()));
        return true;
    }

    @Override
    public List<String> queryTokenList(String tokenValue, boolean sortedDesc) {
        List<String> list = new ArrayList<>();
        for (String key : tokenMap.get().keySet()) {
            if (StringUtils.isNotBlank(tokenValue) && !key.contains(tokenValue)) {
                continue;
            }
            list.add(key);
        }
        if (Boolean.TRUE.equals(sortedDesc)) {
            Collections.reverse(list);
        }
        return list;
    }
}
